/**
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { computed, defineComponent, nextTick, ref, SetupContext, Teleport, watch, onMounted, onUnmounted } from 'vue';
import { PopoverProps, popoverProps } from './popover.props';

import './popover.scss';
import { useHost } from './composition/use-host';
import { usePosition } from './composition/use-position';
import { usePopup } from './composition/use-popup';
import { useResize } from './composition/use-resize';

export default defineComponent({
    name: 'FPopover',
    props: popoverProps,
    emits: ['shown', 'hidden'] as (string[] & ThisType<void>) | undefined,
    setup(props: PopoverProps, context: SetupContext) {
        const arrowRef = ref<any>();
        const popoverRef = ref<any>();
        const reference = ref<HTMLElement>(props.reference);
        const shouldFitWidthToReference = ref(props.keepWidthWithReference);
        const shouldShowTitle = computed(() => !!props.title);

        const hostComposition = useHost(props);
        const { host } = hostComposition;

        const positionComposition = usePosition(props, arrowRef, popoverRef, hostComposition);
        const { position, arrowStyle, popoverStyle, fitToReference, locateToReference } = positionComposition;

        const { showPopover, hidePopverOnClickBodyHandler } = usePopup(props, context, arrowRef, popoverRef,
            reference, shouldFitWidthToReference, positionComposition);

        const { onResize } = useResize(props, context, reference, shouldFitWidthToReference, positionComposition);

        const popoverClass = computed(() => {
            const originPopover = `popover in popover-${position.value}`;
            const bsPopover = `bs-popover-${position.value}`;
            const popoverClassObject = {
                'popover-fitcontent': props.fitContent
            } as any;
            popoverClassObject[originPopover] = true;
            popoverClassObject[bsPopover] = true;
            const customClassArray = (props.class || '').split(' ');
            customClassArray.reduce<Record<string, any>>((classObject, classString) => {
                classObject[classString] = true;
                return classObject;
            }, popoverClassObject);
            return popoverClassObject;
        });

        const popoverContainerClass = computed(() => ({
            'popover-content': true,
            'popover-body': true
        }));

        onMounted(() => {
            props.reference && locateToReference(reference.value);
            if (shouldFitWidthToReference.value) {
                fitToReference(reference.value);
            }
            window.addEventListener('resize', onResize);
        });

        onUnmounted(() => {
            document.body.removeEventListener('click', hidePopverOnClickBodyHandler);
            window.removeEventListener('resize', onResize);
        });

        return () => {
            return (
                <Teleport to={host}>
                    <div ref={popoverRef} class={popoverClass.value} style={popoverStyle.value} v-show={showPopover.value}
                        onClick={(payload: MouseEvent) => { payload.stopPropagation(); }}>
                        <div ref={arrowRef} class="popover-arrow arrow" style={arrowStyle.value}></div>
                        {shouldShowTitle.value && <h3 class="popover-title popover-header">{props.title}</h3>}
                        <div class={popoverContainerClass.value}>{context.slots.default && context.slots?.default()}</div>
                    </div>
                </Teleport>
            );
        };
    }
});
